/**
 * @file spi_screen_driver.c
 * @author WaterFairy
 * @brief
 * @version 1.0.0
 * @date 2024-06-26 17:06:04
 *
 * driverPath: /sys/bus/spi/drivers/spiScreenDriver
 *
 * classPath: /sys/class/spiScreenClass
 * classPath: /sys/devices/virtual/spiScreenClass
 *
 * devicePath: /dev/spiScreenDevice
 * devicePath: /sys/class/spiScreenClass/spiScreenDevice
 * devicePath: /sys/devices/virtual/spiScreenClass/spiScreenDevice
 *
 * @copyright Copyright (c) 2024
 *
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/uaccess.h>
#include <linux/fs.h>

 //字符设备
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/err.h>

#include "spi_screen_driver.h"
#include "GC9A01.h"

dev_t dev_num;                  //字符设备num
struct cdev spi_screen_cdev;     //字符设备
struct class* spi_screen_class;
struct  device* spi_screen_device;

struct  spi_device* spi_screen_spi_device;



uint8_t is_spi_screen_driver_registered = 0;
uint8_t is_spi_screen_device_created = 0;
uint8_t is_spi_screen_class_created = 0;
uint8_t is_spi_screen_cdev_added = 0;
uint8_t is_spi_screen_cdev_region = 0;



struct GC9A01_frame frame = { .start.X = 0,.start.Y = 0,.end.X = 239,.end.Y = 239 };

/**
 * @brief 字符设备打开
 *
 * @param inode
 * @param file
 * @return int
 */
int spi_screen_open(struct inode* inode, struct file* file) {
    uint8_t color[4096] = { 0 };
    size_t i = 0;
    printk("spi_screen_open\n");
    //初始化屏幕

    GC9A01_init_hard();
    GC9A01_init();
    GC9A01_set_frame(frame);

    memset(color, 0x00, 4096);

    for (i = 0; i < 115200 / 4096 + 1; i++)
    {
        if (i == 0)
        {
            GC9A01_write(color, 4096);
        }
        else {
            GC9A01_write_continue(color, 4096);
        }
    }

    GC9A01_set_frame(frame);
    return 0;
}
/**
 * @brief 字符设备读
 *
 * @param file
 * @param buf
 * @param size
 * @param offset
 * @return ssize_t
 */
ssize_t spi_screen_read(struct file* file, char __user* buf, size_t size, loff_t* offset) {
    printk("spi_screen_read\n");
    return 0;
}

/**
 * @brief 字符设备写
 *
 * @param file
 * @param buf
 * @param size
 * @param offset
 * @return ssize_t
 */
ssize_t spi_screen_write(struct file* file, const char __user* buf, size_t size, loff_t* offset) {
    int ret = 0;
    uint8_t  spi_buf[4096] = { 0 };



    ret = copy_from_user(spi_buf, buf, size);


    GC9A01_write_continue((uint8_t*)(spi_buf), size);


    printk("spi_screen_write %d\n", size);

    return size;
}

/**
 * @brief
 *
 * @param inode
 * @param file
 * @return int
 */
int spi_screen_release(struct inode* inode, struct file* file) {
    printk("spi_screen_release\n");
    GC9A01_deinit_hard();
    return 0;
}




//字符设备文件操作
struct file_operations spi_screen_fops = {
.open = spi_screen_open ,
.read = spi_screen_read,
.write = spi_screen_write,
.release = spi_screen_release
};

/**
 * @brief 设备匹配成功后调用 probe:探查
 *
 * @param device
 * @return int
 */
static int screen_probe(struct spi_device* device) {

    int ret;
    printk("screen_probe\n");
    spi_screen_spi_device = device;
    //注册字符设备
    //params1:输出型参数 ，就是要分配的主 次 设备号
    //params2:次设备号的 起始 号
    //params3:几个 次设备号
    //params4:设备名字
    ret = alloc_chrdev_region(&dev_num, 0, 1, "spiScreenCDev");
    if (ret < 0)
    {
        //注册失败
        printk("alloc_chrdev_region error\n");
        return -1;
    }
    is_spi_screen_cdev_region = 1;
    //初始化字符设备
    cdev_init(&spi_screen_cdev, &spi_screen_fops);

    //添加字符设备
    ret = cdev_add(&spi_screen_cdev, dev_num, 1);
    if (ret < 0) {
        //添加失败
        printk("cdev_add error\n");
        return -1;
    }
    is_spi_screen_cdev_added = 1;

    //创建class
    spi_screen_class = class_create(THIS_MODULE, "spiScreenClass");
    if (IS_ERR(spi_screen_class))
    {
        printk("class_create error\n");
        return PTR_ERR(spi_screen_class);
    }
    is_spi_screen_class_created = 1;

    //创建字符设备
    spi_screen_device = device_create(spi_screen_class, NULL, dev_num, NULL, "spiScreenDevice");
    if (IS_ERR(spi_screen_device))
    {
        printk("device_create error\n");
        return PTR_ERR(spi_screen_device);
    }

    is_spi_screen_device_created = 1;


    return 0;

}
static int screen_remove(struct spi_device* device) {


    printk("screen_remove\n");

    return 0;
}



struct of_device_id  screen_of_math_table[] = {
  {.compatible = "my-spiscreen"},     //匹配
  {}  //空 表示结束
};

struct spi_device_id  screen_id_table[] = {
  {.name = "my-spiscreen"},     //匹配
  {}  //空 表示结束
};

struct spi_driver spi_screen_driver = {
    .probe = screen_probe,
    .remove = screen_remove,
    .driver = {
        .name = "spiScreenDriver", //任意名字 /sys/bus/spi/drivers/spiScreen
        .owner = THIS_MODULE,
        .of_match_table = screen_of_math_table  //设备树匹配规则

    },
    .id_table = screen_id_table,//非设备树匹配规则

};

static int __init spi_screen_init(void) {
    int ret = 0;
    printk("spi_screen_init\n");

    //注册driver
    ret = spi_register_driver(&spi_screen_driver);
    if (ret < 0)
    {
        printk("spi_register_driver err\n");
        return -1;
    }
    is_spi_screen_driver_registered = 1;
    printk("spi_screen_init success\n");

    return 0;

}

static void __exit spi_screen_exit(void) {


    if (is_spi_screen_device_created) { device_destroy(spi_screen_class, dev_num); }
    if (is_spi_screen_class_created) { class_destroy(spi_screen_class); }
    if (is_spi_screen_cdev_added) { cdev_del(&spi_screen_cdev); }
    if (is_spi_screen_cdev_region) { unregister_chrdev_region(dev_num, 1); }
    if (is_spi_screen_driver_registered) { spi_unregister_driver(&spi_screen_driver); }


    printk("spi_screen_exit\n");
}


module_init(spi_screen_init);
module_exit(spi_screen_exit);
MODULE_LICENSE("GPL");
